package cn.sytton.taffe.web.flw.service.impl;

import cn.sytton.taffe.framework.base.entity.PageParam;
import cn.sytton.taffe.framework.base.service.BaseService;
import cn.sytton.taffe.framework.util.SecurityUtil;
import cn.sytton.taffe.web.flw.entity.*;
import cn.sytton.taffe.web.flw.mapper.FlowDefMapper;
import cn.sytton.taffe.web.flw.service.IFlowRunService;
import com.github.pagehelper.PageInfo;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.camunda.bpm.engine.history.HistoricTaskInstance;
import org.camunda.bpm.engine.history.HistoricVariableInstance;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.runtime.VariableInstance;
import org.camunda.bpm.engine.task.Task;
import org.camunda.bpm.engine.task.TaskQuery;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static cn.sytton.taffe.web.flw.help.constant.*;

/**
 * 流程运行 服务层
 *
 * @author skyrock
 */
@Service
@RequiredArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class FlowRunService extends BaseService<FlowDefMapper, FlowDefEntity> implements IFlowRunService {

    private final CamundaService camundaService;

    @Override
    public PageInfo<FlowTaskEntity> findTodoPage(PageParam pp) {
        PageInfo<FlowTaskEntity> page = new PageInfo<>();
        TaskQuery taskQuery = camundaService.task().createTaskQuery()
                .taskCandidateUser(SecurityUtil.getUserId())
                .includeAssignedTasks()
                .active();

        long total = taskQuery.count();
        page.setTotal(total);

        if (total > 0) {
            ProcessPageEntity ppe = new ProcessPageEntity(pp);
            List<Task> tasks = taskQuery
                    .orderByTaskCreateTime().desc()
                    .orderByTaskId().desc()
                    .listPage(ppe.getStart(), ppe.getEnd());
            List<FlowTaskEntity> fTasks = new ArrayList<>();
            for (Task t : tasks) {
                List<VariableInstance> viList = camundaService.runtime()
                        .createVariableInstanceQuery()
                        .processInstanceIdIn(t.getProcessInstanceId())
                        .list();
                Map<String, Object> processVariable = new HashMap<>();
                FormInstanceEntity fEntity = new FormInstanceEntity();
                for (VariableInstance vi : viList) {
                    if (vi.getName().equals(FLOW_VARS_FORM_INSTANCE)) {
                        fEntity = (FormInstanceEntity) vi.getValue();
                    } else {
                        processVariable.put(vi.getName(), vi.getValue());
                    }
                }
                fTasks.add(new FlowTaskEntity()
                        .setTaskId(t.getId())
                        .setTaskName(t.getName())
                        .setTaskDefKey(t.getTaskDefinitionKey())
                        .setTaskStartTime(t.getCreateTime())
                        .setAssignee(t.getAssignee())
                        //.setAssigneeName()
                        .setProcessInstanceId(t.getProcessInstanceId())
                        .setProcessDefId(t.getProcessDefinitionId())
                        .setFormInstance(fEntity)
                        .setProcessVariables(processVariable)
                        .setTenantId(t.getTenantId()));
            }
            page.setList(fTasks);
        }
        return page;
    }

    @Override
    public String start(FlowInstanceVariableEntity fiv, String processDefKey, String processDefId) {
        ProcessInstance pi = null;
        fiv.getVariables().put(FLOW_VARS_FORM_INSTANCE, fiv.getFormInstance());
        fiv.getVariables().put(FLOW_VARS_APPLY_USER_ID, fiv.getFormInstance().getApplyUserId());
        if (StringUtils.isNoneEmpty(processDefId)) {
            pi = camundaService.runtime().startProcessInstanceById(processDefId, fiv.getFormInstance().getFormInstanceId(),  fiv.getVariables());
        } else if (StringUtils.isNoneEmpty(processDefKey)) {
            pi = camundaService.runtime().startProcessInstanceByKey(processDefKey, fiv.getFormInstance().getFormInstanceId(), fiv.getVariables());
        }
        if (pi != null) {
            // 提交第一个申请节点
            Task task = camundaService.task().createTaskQuery().processInstanceId(pi.getId()).active().list().get(0);

            Map<String, Object> vars = new HashMap<>();
            vars.put(FLOW_VARS_APPLY_USER_ID, fiv.getFormInstance().getApplyUserId());
            vars.put(FLOW_VARS_APPLY_USER_NAME, fiv.getFormInstance().getApplyUserName());
            camundaService.task().setVariablesLocal(task.getId(), vars);
            camundaService.task().complete(task.getId());

            return pi.getProcessInstanceId();
        } else {
            return "";
        }
    }

    @Override
    public void complete(FlowTaskVariableEntity ftv) {
        // todo 需要加点什么其他的流程变量？
        camundaService.task().setVariablesLocal(ftv.getTaskId(), ftv.getVariables());
        camundaService.task().claim(ftv.getTaskId(), SecurityUtil.getUserId());
        camundaService.task().complete(ftv.getTaskId(), ftv.getVariables());
    }

    @Override
    public List<FlowTaskEntity> findTaskByProcessInstanceId(String processInstanceId) {
        List<Task> ruTasks = camundaService.task().createTaskQuery()
                .processInstanceId(processInstanceId)
                .active()
                .orderByTaskCreateTime().desc()
                .list();

        List<HistoricTaskInstance> hiTasks = camundaService.history().createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .finished()
                .orderByHistoricTaskInstanceEndTime().desc()
                .list();

        List<FlowTaskEntity> fTasks = new ArrayList<>();
        for (Task t : ruTasks) {
            fTasks.add(new FlowTaskEntity()
                    .setTaskId(t.getId())
                    .setTaskName(t.getName())
                    .setTaskDefKey(t.getTaskDefinitionKey())
                    .setTaskStartTime(t.getCreateTime())
                    .setProcessDefId(t.getProcessDefinitionId()));
        }
        for (HistoricTaskInstance ht : hiTasks) {
            List<HistoricVariableInstance> hiVars = camundaService.history()
                    .createHistoricVariableInstanceQuery()
                    .taskIdIn(ht.getId())
                    .list();
            Map<String, Object> taskVars = new HashMap<>();
            for (HistoricVariableInstance hiVar : hiVars) {
                String name = hiVar.getName();
                if (!name.equalsIgnoreCase(FLOW_VARS_FORM_INSTANCE)) {
                    taskVars.put(name, hiVar.getValue());
                }
            }
            fTasks.add(new FlowTaskEntity()
                    .setTaskId(ht.getId())
                    .setTaskName(ht.getName())
                    .setTaskDefKey(ht.getTaskDefinitionKey())
                    .setTaskEndTime(ht.getEndTime())
                    .setProcessDefId(ht.getProcessDefinitionId())
                    .setTaskVariables(taskVars));
        }
        return fTasks;
    }

    @Override
    public List<Map<String, Object>> findTaskById(String taskId) {
        Task task = camundaService.task().createTaskQuery().taskIdIn(taskId).singleResult();
        Map<String, Object> map = new HashMap<>();
        //todo 暂时无用
        return new ArrayList<>();
    }
}
